#!/usr/bin/python
# -*- coding: UTF-8 -*-

import json
import sys
import os

class FileGenerator:

    def ChangeToCamelCase(self, name):
        strlist = name.split('_')
        retstr = ""
        for value in strlist :
            retstr = retstr + value.capitalize()
        return retstr

    def __init__(self, file_name, cpp_json, pb_json = None):
        self.file_name = file_name
        self.file_name_split = file_name.split(".")
        self.cpp_json = cpp_json
        self.pb_jsoon = pb_json
        self.has_rpc = False
        file = open(self.file_name_split[0] + ".service.cs", "w")
        file.write("// Machine generated code\n\n")
        file.write("#region Machine generated code\n\n")
        file.write("using kratos;\n")
        file.write("using RingBuffer;\n")
        file.write("using System;\n")
        file.write("using System.Threading.Tasks;\n")
        file.write("using System.Collections.Generic;\n\n")
        self.gen_enum()
        self.gen_struct()
        self.gen_struct_impl()
        if self.has_rpc:
            file.write("using rpc;\n\n")
        self.gen_call_param_delegate_func(file)
        self.gen_call_return_delegate_func(file)
        self.gen_service_proxy_api(file)
        self.gen_service_proxy_impl(file)
        self.gen_param_return_register(file)
        self.gen_service_caller(file)
        file.write("#endregion Machine generated code\n\n")
        file.close()
        self.gen_service_api()
        self.gen_rpc_register()

    def gen_enum(self):
        if ("enums" in self.cpp_json) and (self.cpp_json["enums"] is not None):
            self.has_rpc = True
            for enum in self.cpp_json["enums"]:
                file = open(enum["name"] + ".enum.cs", "w")
                file.write('namespace rpc {\n\n')
                file.write("public enum " + enum["name"] + " {\n")
                if "fields" in enum:
                    for field in enum["fields"]:
                        file.write("    " + field["name"] + " = " + str(field["value"]) + ",\n")
                file.write("}\n\n")
                file.write('}\n\n')        

    def gen_struct(self):
        if (not ("structs" in self.cpp_json)) or (self.cpp_json["structs"] is None):
            return
        self.has_rpc = True
        for struct in self.cpp_json["structs"]:
            file = open(struct["name"] + ".struct.cs", "w")
            file.write("using System.Threading.Tasks;\n")
            file.write("using System.Collections.Generic;\n\n")
            file.write("namespace rpc {\n\n")
            file.write("public abstract class " + struct["name"] + " {\n")
            for field in struct["fields"]:
                if field["IdlType"] == "seq":
                    Idlstr = "    public abstract IList<"
                    if field["key"]["IdlType"] == "enum":
                        Idlstr += "Rpcdata."
                    Idlstr += field["key"]["type"] + "> " + self.ChangeToCamelCase(field["name"]) + " { get; }\n"
                    file.write(Idlstr)
                elif field["IdlType"] == "set":
                    #file.write("    public abstract ISet<" + field["key"]["type"] + "> " + self.ChangeToCamelCase(field["name"]) + " { get; }\n")
                    Idlstr = "    public abstract ISet<"
                    if field["key"]["IdlType"] == "enum":
                        Idlstr += "Rpcdata."
                    Idlstr += field["key"]["type"] + "> " + self.ChangeToCamelCase(field["name"]) + " { get; }\n"
                    file.write(Idlstr)
                elif field["IdlType"] == "dict":
                    #file.write("    public abstract IDictionary<" + field["key"]["type"] + "," + field["value"]["type"] + "> " + self.ChangeToCamelCase(field["name"]) + " { get; }\n")
                    Idlstr = "    public abstract IDictionary<"
                    if field["key"]["IdlType"] == "enum":
                        Idlstr += "Rpcdata."
                    Idlstr += field["key"]["type"] + ","
                    if field["value"]["IdlType"] == "enum":
                        Idlstr += "Rpcdata."
                    Idlstr += field["value"]["type"] + "> " + self.ChangeToCamelCase(field["name"]) + " { get; }\n"
                    file.write(Idlstr)
                elif field["IdlType"] == "struct":
                    file.write("    public abstract " + field["type"] + " " + self.ChangeToCamelCase(field["name"]) + " { get; }\n")
                elif field["IdlType"] == "enum":
                    file.write("    public abstract " + "Rpcdata." + field["type"] + " " + self.ChangeToCamelCase(field["name"]) + " { get; set; }\n")
                else:
                    file.write("    public abstract " + field["type"] + " " + self.ChangeToCamelCase(field["name"]) + " { get; set; }\n")
            file.write("    public static " + struct["name"] + " Create() {\n")
            file.write("        return new " + struct["name"] + "Impl();\n")
            file.write("    }\n")
            file.write("}\n\n")
            file.write("}\n\n")

    def gen_struct_impl(self):
        if (not ("structs" in self.cpp_json)) or (self.cpp_json["structs"] is None):
            return
        for struct in self.cpp_json["structs"]:
            file = open(struct["name"] + ".struct.impl.cs", "w")
            file.write("using System.Threading.Tasks;\n")
            file.write("using System.Collections.Generic;\n\n")
            file.write("namespace rpc {\n\n")
            file.write("internal class " + struct["name"] + "Impl : " + struct["name"] + " {\n")
            file.write("    " +'Rpcdata.'+struct["name"] + " value_;\n")
            for field in struct["fields"]:
                if field["IdlType"] == "seq" and field["key"]["IdlType"] == "struct":
                    type = "List<" + field["key"]["type"] + ">"
                    file.write("    private " + type + " inter_" + field["name"] + " = new " + type + "();\n")
                elif field["IdlType"] == "dict" and field["value"]["IdlType"] == "struct":
                    type = "Dictionary<" + field["key"]["type"] + "," + field["value"]["type"] + ">"
                    file.write("    private " + type + " inter_" + field["name"] + " = new " + type + "();\n")
                elif field["IdlType"] == "struct":
                    file.write("    private " + field["type"] + "Impl inter_" + field["name"] + " = new " + field["type"] + "Impl();\n")
                elif field["IdlType"] == "set":
                    file.write("    private HashSet<" + field["key"]["type"] + "> inter_" + field["name"] + " = new HashSet<" + field["key"]["type"] + ">();\n")
            file.write("    public " + struct["name"] + "Impl() {\n")
            file.write("        value_ = new "+'Rpcdata.' + struct["name"] + "();\n")
            file.write("    }\n")
            file.write("    internal " + struct["name"] + "Impl(" + 'Rpcdata.'+struct["name"] + " v) {\n")
            file.write("        value_ = v;\n")
            for field in struct["fields"]:
                if field["IdlType"] == "seq" and field["key"]["IdlType"] == "struct":
                    file.write("        foreach (var k in value_." + self.ChangeToCamelCase(field["name"]) + ") {\n")
                    file.write("            inter_" + field["name"] + ".Add(new " + field["key"]["type"] + "Impl(k));\n")
                    file.write("        }\n")
                elif field["IdlType"] == "dict" and field["value"]["IdlType"] == "struct":
                    file.write("        foreach (var k in value_." + self.ChangeToCamelCase(field["name"]) + ") {\n")
                    file.write("            inter_" + field["name"] + ".Add(k.Key, new " + field["value"]["type"] + "Impl(k.Value));\n")
                    file.write("        }\n")
                elif field["IdlType"] == "set":
                    file.write("        foreach (var k in value_." + self.ChangeToCamelCase(field["name"]) + ") {\n")
                    file.write("            inter_" + field["name"] + ".Add(k);\n")
                    file.write("        }\n")
                elif field["IdlType"] == "struct":
                    file.write("        inter_" + field["name"] + ".PBObject = value_." + self.ChangeToCamelCase(field["name"]) + ";\n")
            file.write("    }\n")
            file.write("    internal " + 'Rpcdata.'+struct["name"] + " PBObject {\n")
            file.write("        get {\n")
            for field in struct["fields"]:
                if field["IdlType"] == "seq" and field["key"]["IdlType"] == "struct":
                    file.write("            foreach (var k in inter_" + field["name"] + ") {\n")
                    file.write("                value_." + self.ChangeToCamelCase(field["name"]) + ".Add(((" + field["key"]["type"] + "Impl)k).PBObject);\n")
                    file.write("            }\n")
                elif field["IdlType"] == "dict" and field["value"]["IdlType"] == "struct":
                    file.write("            foreach (var k in inter_" + field["name"] + ") {\n")
                    file.write("                value_." + self.ChangeToCamelCase(field["name"]) + ".Add(k.Key, ((" + field["value"]["type"] + "Impl)k.Value).PBObject);\n")
                    file.write("            }\n")
                elif field["IdlType"] == "set":
                    file.write("            foreach(var k in inter_" + field["name"] + ") {\n")
                    file.write("                value_." + self.ChangeToCamelCase(field["name"]) + ".Add(k);\n")
                    file.write("            }\n")
                elif field["IdlType"] == "struct":
                    file.write("            value_." + self.ChangeToCamelCase(field["name"]) + " = inter_" + field["name"] + ".PBObject;\n")
            file.write("            return value_;\n")
            file.write("        }\n")
            file.write("        set {\n")
            file.write("            value_ = value;\n")
            for field in struct["fields"]:
                if field["IdlType"] == "seq" and field["key"]["IdlType"] == "struct":
                    file.write("            foreach (var k in value_." + self.ChangeToCamelCase(field["name"]) + ") {\n")
                    file.write("                inter_" + field["name"] + ".Add(new " + field["key"]["type"] + "Impl(k));\n")
                    file.write("            }\n")
                elif field["IdlType"] == "dict" and field["value"]["IdlType"] == "struct":
                    file.write("            foreach (var k in value_." + self.ChangeToCamelCase(field["name"]) + ") {\n")
                    file.write("                inter_" + field["name"] + ".Add(k.Key, new " + field["value"]["type"] + "Impl(k.Value));\n")
                    file.write("            }\n")
                elif field["IdlType"] == "set":
                    file.write("            foreach (var k in value_." + self.ChangeToCamelCase(field["name"]) + ") {\n")
                    file.write("                inter_" + field["name"] + ".Add(k);\n")
                    file.write("            }\n")
                elif field["IdlType"] == "struct":
                    file.write("            inter_" + field["name"] + ".PBObject = value_." + self.ChangeToCamelCase(field["name"]) + ";\n")
            file.write("        }\n")
            file.write("    }\n")
            for field in struct["fields"]:
                if field["IdlType"] == "seq":
                    type = "IList<"
                    if field["key"]["IdlType"] == "enum":
                        type += "Rpcdata."
                    type += field["key"]["type"] + ">"
                    if field["key"]["IdlType"] == "struct":
                        file.write("    public override " + type + " " + self.ChangeToCamelCase(field["name"]) + " { get { return inter_" + field["name"] +"; } }\n")
                    else:
                        file.write("    public override " + type + " " + self.ChangeToCamelCase(field["name"]) + " { get { return (" + type +")value_." + self.ChangeToCamelCase(field["name"]) + "; } }\n")
                elif field["IdlType"] == "dict":
                    type = "IDictionary<" + field["key"]["type"] + ","
                    if field["value"]["IdlType"] == "enum":
                        type += "Rpcdata."
                    type += field["value"]["type"] + ">"
                    if field["value"]["IdlType"] == "struct":
                        file.write("    public override " + type + " " + self.ChangeToCamelCase(field["name"]) + " { get { return inter_" + field["name"] +"; } }\n")
                    else:
                        file.write("    public override " + type + " " + self.ChangeToCamelCase(field["name"]) + " { get { return (" + type +")value_." + self.ChangeToCamelCase(field["name"]) + "; } }\n")
                elif field["IdlType"] == "struct":
                    file.write("    public override " + field["type"] + " " + self.ChangeToCamelCase(field["name"]) + "{ get { return inter_" + field["name"] + "; } }\n")
                elif field["IdlType"] == "set":
                    file.write("    public override ISet<" + field["key"]["type"] + "> " + self.ChangeToCamelCase(field["name"]) + " { get { return (ISet<" + field["key"]["type"] + ">)inter_" + field["name"] + "; } }\n")
                elif field["IdlType"] == "enum":
                    file.write("    public override " + "Rpcdata." + field["type"] + " " + self.ChangeToCamelCase(field["name"]) + " { get { return value_." + self.ChangeToCamelCase(field["name"]) + "; } set { value_." + self.ChangeToCamelCase(field["name"]) + " = value; } }\n")
                else:
                    file.write("    public override " + field["type"] + " " + self.ChangeToCamelCase(field["name"]) + " { get { return value_." + self.ChangeToCamelCase(field["name"]) + "; } set { value_." + self.ChangeToCamelCase(field["name"]) + " = value; } }\n")
            file.write("}\n\n")
            file.write("}\n\n")

    def gen_call_param_delegate_func(self, file):
        if (not ("services" in self.cpp_json)) or (self.cpp_json["services"] is None):
            return
        for service in self.cpp_json["services"]:
            file.write("namespace "+ self.file_name_split[0]  +" {\n\n")
            for method in service["methods"]:
                file.write("internal static class " + service["name"] + "_" + method["name"] + "_delegate {\n")
                file.write("    public static " + service["name"] + "_" + method["name"] + "_CallParam NewParam() {\n")
                file.write("        return kratos.ObjectPool<" + service["name"] + "_" + method["name"] + "_CallParam>.Get();\n")
                file.write("    }\n")
                file.write("    public static " + service["name"] + "_" + method["name"] + "_CallReturn NewReturn() {\n")
                file.write("        return kratos.ObjectPool<" + service["name"] + "_" + method["name"] + "_CallReturn>.Get();\n")
                file.write("    }\n")
                file.write("}\n\n")
                file.write("internal class " + service["name"] + "_" + method["name"] + "_CallParam : CallParam, kratos.IRecycle {\n")
                file.write("    uint call_id_ = 0;\n")
                file.write("    uint service_id_ = 0;\n")
                file.write("    uint length_ = 0;\n")
                file.write("    bool debug_loop_ = false;\n")
                file.write("    public kratos.RecycleFunc RecycleMethod {\n")
                file.write("        get => kratos.ObjectPool<" + service["name"] + "_" + method["name"] + "_CallParam>.Recycle;\n")
                file.write("    }\n")
                n = 0
                no_param = False
                for arg in method["arguments"]:
                    if arg["IdlType"] == "void":
                        no_param = True
                        break
                    n += 1
                    if arg["IdlType"] == "seq" or arg["IdlType"] == "set":
                        file.write("    public " + arg["type"] + "<" + arg["key"]["type"] + ">" + " arg" + str(n) + ";\n")
                    elif arg["IdlType"] == "dict":
                        file.write("    public " + arg["type"] + "<" + arg["key"]["type"] + "," + arg["value"]["type"] + ">" + " arg" + str(n) + ";\n")
                    else:
                        file.write("    public " + arg["type"] + " arg" + str(n) + ";\n")
                if no_param:
                    file.write("    public bool NoParam { get => true; }\n")
                else:
                    file.write("    public bool NoParam { get => false; }\n")
                file.write("    public uint CallID { get => call_id_; set => call_id_ = value; }\n")
                file.write("    public ulong ServiceUUID { get => " + service["uuid"] + "; }\n")
                file.write("    public uint MethodID { get => " + str(method["index"]) + "; }\n")
                file.write("    public uint ServiceID { get => service_id_; set => service_id_ = value; }\n")
                file.write("    public uint Length { get => length_; set => length_ = value; }\n")
                file.write("    public void deserialize(GrowingRingBuffer<byte> rb, uint length) {\n")
                file.write("        " + service["name"] + "CallParamSerializer." + method["name"] + "_deserialize(this, rb, length);\n")
                file.write("    }\n")
                file.write("    public byte[] serialize() {\n")
                file.write("        return " + service["name"] + "CallParamSerializer." + method["name"] + "_serialize(this);\n")
                file.write("    }\n")
                file.write("    public bool IsDebugLoop {\n")
                file.write("        get => debug_loop_;\n")
                file.write("        set => debug_loop_ = value;\n")
                file.write("    }\n")
                file.write("    public void OnReset() {\n")
                file.write("        call_id_ = 0;\n")
                file.write("        service_id_ = 0;\n")
                file.write("        length_ = 0;\n")
                n = 0
                for arg in method["arguments"]:
                    if arg["IdlType"] == "void":
                        break;
                    n += 1
                    if arg["IdlType"] == "seq" or arg["IdlType"] == "set":
                        file.write("        arg" + str(n) + "?.Clear();\n")
                    elif arg["IdlType"] == "dict":
                        file.write("        arg" + str(n) + "?.Clear();\n")
                    elif arg["IdlType"] == "struct" or arg["IdlType"] == "string":
                        file.write("        arg" + str(n) + " = null;\n")
                    elif arg["IdlType"] == "bool":
                        file.write("        arg" + str(n) + " = false;\n")
                    else:
                        file.write("        arg" + str(n) + " = 0;\n")
                file.write("    }\n")
                file.write("}\n\n")
            file.write("}\n\n")

    def gen_call_return_delegate_func(self, file):
        if (not ("services" in self.cpp_json)) or (self.cpp_json["services"] is None):
            return
        for service in self.cpp_json["services"]:
            file.write("namespace "+ self.file_name_split[0]  +" {\n\n")
            for method in service["methods"]:
                file.write("internal class " + service["name"] + "_" + method["name"] + "_CallReturn : CallReturn, IRecycle {\n")
                file.write("    uint call_id_ = 0;\n")
                file.write("    uint service_id_ = 0;\n")
                file.write("    uint length_ = 0;\n")
                file.write("    uint error_id_ = (uint)RpcError.rpc_ok;\n")
                file.write("    public kratos.RecycleFunc RecycleMethod {\n")
                file.write("        get => kratos.ObjectPool<" + service["name"] + "_" + method["name"] + "_CallReturn>.Recycle;\n")
                file.write("    }\n")
                if method["retType"]["IdlType"] != "void":
                    ret_string = ""
                    if method["retType"]["IdlType"] == "seq" or method["retType"]["IdlType"] == "set":
                        ret_string = method["retType"]["type"] + "<" + method["retType"]["key"]["type"] + ">"
                    elif method["retType"]["IdlType"] == "dict":
                        ret_string = method["retType"]["type"] + "<" + method["retType"]["key"]["type"] + "," + method["retType"]["value"]["type"] + ">"
                    elif method["retType"]["IdlType"] == "string" or method["retType"]["IdlType"] == "struct":
                        ret_string = method["retType"]["type"]
                    else:
                        ret_string = method["retType"]["type"]
                    file.write("    public " + ret_string + " ret;\n")
                no_return = False
                if method["retType"]["IdlType"] == "void":
                    no_return = True;
                if no_return:
                    file.write("    public bool NoReturn { get => true; }\n")
                else:
                    file.write("    public bool NoReturn { get => false; }\n")
                file.write("    public uint CallID { get => call_id_; set => call_id_ = value; }\n")
                file.write("    public ulong ServiceUUID { get => " + service["uuid"] + "; }\n")
                file.write("    public uint MethodID { get => " + str(method["index"]) + "; }\n")
                file.write("    public uint ServiceID { get => service_id_; set => service_id_ = value; }\n")
                file.write("    public uint Length { get => length_; set => length_ = value; }\n")
                file.write("    public uint ErrorID { get => error_id_; set => error_id_ = value; }\n")
                file.write("    public void deserialize(GrowingRingBuffer<byte> rb, uint length) {\n")
                file.write("        " + service["name"] + "CallReturnSerializer." + method["name"] + "_deserialize(this, rb, length);\n")
                file.write("    }\n")
                file.write("    public byte[] serialize() {\n")
                file.write("        return " + service["name"] + "CallReturnSerializer." + method["name"] + "_serialize(this);\n")
                file.write("    }\n")
                file.write("    public void OnReset() {\n")
                file.write("        call_id_ = 0;\n")
                file.write("        service_id_ = 0;\n")
                file.write("        length_ = 0;\n")
                file.write("        error_id_ = 0;\n")
                if not no_return:
                    if method["retType"]["IdlType"] == "seq" or method["retType"]["IdlType"] == "set":
                        file.write("        ret = null;\n")
                    elif method["retType"]["IdlType"] == "dict":
                        file.write("        ret = null;\n")
                    elif method["retType"]["IdlType"] == "string" or method["retType"]["IdlType"] == "struct":
                        file.write("        ret = null;\n")
                    elif method["retType"]["IdlType"] == "bool":
                        file.write("        ret = false;\n")
                    else:
                        file.write("        ret = 0;\n")
                file.write("    }\n")
                file.write("}\n\n")
            file.write("}\n\n")

    def gen_service_api(self):
        if (not ("services" in self.cpp_json)) or (self.cpp_json["services"] is None):
            return
        for service in self.cpp_json["services"]:
            no_service = False
            if "notations" in service:
                for notation in service["notations"]:
                    if "no_service" in notation:
                        for name in notation["no_service"]:
                            if name == "csharp":
                                no_service = True
                                break
            if no_service:
                continue
            inter_content = ""
            content = ""
            inter_content += "internal abstract class " + service["name"] + " : IService {\n"
            if service["methods"] is not None:
                inter_content += "    uint id_ = 0;\n"
                inter_content += "    public ulong UUID { get { return " + service["uuid"] + "; } }\n"
                inter_content += "    public uint ID { get { return id_; } set { id_ = value; } }\n"
                inter_content += "    public virtual void OnTick(long ms) {}\n"
                for method in service["methods"]:
                    inter_content += "    public abstract "                
                    if method["retType"]["IdlType"] == "seq" or method["retType"]["IdlType"] == "set":
                        inter_content += method["retType"]["type"] + "<" + method["retType"]["key"]["type"] + "> "
                    elif method["retType"]["IdlType"] == "dict":
                        inter_content += method["retType"]["type"] + "<" + method["retType"]["key"]["type"] + "," + method["retType"]["value"]["type"] + "> "
                    elif method["retType"]["IdlType"] == "string" or method["retType"]["IdlType"] == "struct":
                        inter_content += method["retType"]["type"] + " "
                    else:
                        inter_content += method["retType"]["type"] + " "
                    inter_content += method["name"] + "("
                    n = 0
                    for arg in method["arguments"]:
                        if arg["IdlType"] == "void":
                            break;
                        if arg["IdlType"] == "seq" or arg["IdlType"] == "set":
                            inter_content += arg["type"] + "<" + arg["key"]["type"] + ">"
                        elif arg["IdlType"] == "dict":
                            inter_content += arg["type"] + "<" + arg["key"]["type"] + "," + arg["value"]["type"] + ">"
                        elif arg["IdlType"] == "struct" or arg["IdlType"] == "string":
                            inter_content += arg["type"]
                        else:
                            inter_content += arg["type"]
                        n += 1
                        inter_content += " arg" + str(n);
                        if n != len(method["arguments"]):
                            inter_content += ","
                    inter_content += ");\n"
                inter_content += "}\n\n"
            content += "internal class " + service["name"] + "Impl : " + service["name"] + " {\n"
            content += "    public " + service["name"] + "Impl() {\n}\n"
            content += "    public override void OnTick(long ms) {\n}\n"
            if service["methods"] is not None:
                for method in service["methods"]:
                    content += "    " + "public override "
                    if method["retType"]["IdlType"] == "seq" or method["retType"]["IdlType"] == "set":
                        content +=  method["retType"]["type"] + "<" + method["retType"]["key"]["type"] + "> "   
                    elif method["retType"]["IdlType"] == "dict":
                        content += method["retType"]["type"] + "<" + method["retType"]["key"]["type"] + "," + method["retType"]["value"]["type"] + "> "                 
                    elif method["retType"]["IdlType"] == "string" or method["retType"]["IdlType"] == "struct":
                        content += method["retType"]["type"] + " "
                    else:
                        content += method["retType"]["type"] + " "
                    content += method["name"] + "("
                    n = 0
                    for arg in method["arguments"]:
                        if arg["IdlType"] == "void":
                            break;
                        n += 1
                        if arg["IdlType"] == "seq" or arg["IdlType"] == "set":
                            content += " " + arg["type"] + "<" + arg["key"]["type"] + ">"
                        elif arg["IdlType"] == "dict":
                            content += " " + arg["type"] + "<" + arg["key"]["type"] + "," + arg["value"]["type"] + ">"
                        elif arg["IdlType"] == "struct" or arg["IdlType"] == "string":
                            content += " " + arg["type"]
                        else:
                            content += " " + arg["type"]
                        content += " arg" + str(n);
                        if n != len(method["arguments"]):
                            content += ","
                    content += ") {\n        throw new System.NotImplementedException();\n    }\n"
            content += "}\n\n"
            interface_file_name = self.file_name_split[0] + ".service." + service["name"] +".cs"
            impl_file_name = self.file_name_split[0] + ".service." + service["name"] +".impl.cs"
            inter_file = open(interface_file_name, "w")
            inter_file.write("// Machine generated code\n\n")
            inter_file.write("using System.Collections.Generic;\n")
            inter_file.write("using kratos;\n\n")
            inter_file.write("namespace rpc {\n\n")
            inter_file.write("//interface->\n")
            inter_file.write(inter_content)
            inter_file.write("//interface<-\n")
            inter_file.write("}\n\n")
            inter_file.close()
            impl_file = open(impl_file_name, "w")
            impl_file.write("// Machine generated code\n\n")
            impl_file.write("using System.Collections.Generic;\n")
            impl_file.write("using kratos;\n\n")
            impl_file.write("namespace rpc {\n\n")
            impl_file.write("//implementation->\n")
            impl_file.write(content)
            impl_file.write("//implementation<-\n")
            impl_file.write("}\n\n")
            impl_file.close()

    def gen_service_proxy_api(self, file):
        if (not ("services" in self.cpp_json)) or (self.cpp_json["services"] is None):
            return
        for service in self.cpp_json["services"]:
            file.write("namespace "+ self.file_name_split[0]  +" {\n\n")
            file.write("public abstract class " + service["name"] + "Proxy {\n")
            for method in service["methods"]:
                file.write("    public abstract ")
                if method["retType"]["IdlType"] == "void":
                    if not ("oneway" in method):
                        file.write("Task ")
                    else:
                        file.write("void ")
                elif method["retType"]["IdlType"] == "seq" or method["retType"]["IdlType"] == "set":
                    file.write("Task<" + method["retType"]["type"] + "<" + method["retType"]["key"]["type"] + ">> ")
                elif method["retType"]["IdlType"] == "dict":
                    file.write("Task<" + method["retType"]["type"] + "<" + method["retType"]["key"]["type"] + "," + method["retType"]["value"]["type"] + ">> ")
                elif method["retType"]["IdlType"] == "string" or method["retType"]["IdlType"] == "struct":
                    file.write("Task<" + method["retType"]["type"] + "> ")
                else:
                    file.write("Task<" + method["retType"]["type"] + "> ")
                file.write(method["name"] + "(")
                n = 0
                no_param = False
                for arg in method["arguments"]:
                    if arg["IdlType"] == "void":
                        no_param = True
                        break;
                    if arg["IdlType"] == "seq" or arg["IdlType"] == "set":
                        file.write(arg["type"] + "<" + arg["key"]["type"] + ">")
                    elif arg["IdlType"] == "dict":
                        file.write(arg["type"] + "<" + arg["key"]["type"] + "," + arg["value"]["type"] + ">")
                    elif arg["IdlType"] == "struct" or arg["IdlType"] == "string":
                        file.write(arg["type"])
                    else:
                        file.write(arg["type"])
                    n += 1
                    file.write(" arg" + str(n))
                    if n != len(method["arguments"]):
                        file.write(",")
                if not ("oneway" in method):
                    if no_param:
                        file.write("long timeout = " + str(method["timeout"]) + ");\n")
                    else:
                        file.write(", long timeout = " + str(method["timeout"]) + ");\n")
                else:
                    file.write(");\n")
            file.write("}\n\n")
            file.write("public partial class Proxy {\n")
            file.write("    public static " + service["name"] + "Proxy Get" + service["name"] + "(bool debug_loop = false) {\n")
            file.write("        if (!Singleton<Rpc>.Instance.IsConnected && !debug_loop) { return null; }\n")
            file.write("        return new " + service["name"] + "ProxyImpl(Singleton<Rpc>.Instance.Client, debug_loop);\n")
            file.write("    }\n")
            file.write("}\n\n")
            file.write("}\n\n")

    def gen_service_proxy_impl(self, file):
        if (not ("services" in self.cpp_json)) or (self.cpp_json["services"] is None):
            return
        for service in self.cpp_json["services"]:
            file.write("namespace "+ self.file_name_split[0]  +" {\n\n")
            file.write("internal class " + service["name"] + "ProxyImpl : " + service["name"] + "Proxy {\n")
            file.write("    IClient client_ = null;\n")
            file.write("    bool debug_loop_ = false;\n")
            file.write("    public " + service["name"] + "ProxyImpl(IClient client, bool debug_loop = false) {\n")
            file.write("        debug_loop_ = debug_loop;\n")
            file.write("        client_ = client;\n")
            file.write("    }\n")
            ret_string = ""
            for method in service["methods"]:
                if not ("oneway" in method):
                    file.write("    public override async ")      
                else:
                    file.write("    public override ")
                if method["retType"]["IdlType"] == "void":
                    if not ("oneway" in method):
                        file.write("Task ")
                    else:
                        file.write("void ")
                elif method["retType"]["IdlType"] == "seq" or method["retType"]["IdlType"] == "set":
                    ret_string = method["retType"]["type"] + "<" + method["retType"]["key"]["type"] + ">"
                    file.write("Task<" + method["retType"]["type"] + "<" + method["retType"]["key"]["type"] + ">> ")
                elif method["retType"]["IdlType"] == "dict":
                    ret_string = method["retType"]["type"] + "<" + method["retType"]["key"]["type"] + "," + method["retType"]["value"]["type"] + ">"
                    file.write("Task<" + method["retType"]["type"] + "<" + method["retType"]["key"]["type"] + "," + method["retType"]["value"]["type"] + ">> ")
                elif method["retType"]["IdlType"] == "string" or method["retType"]["IdlType"] == "struct":
                    ret_string = method["retType"]["type"]
                    file.write("Task<" + method["retType"]["type"] + "> ")
                else:
                    ret_string = method["retType"]["type"]
                    file.write("Task<" + method["retType"]["type"] + "> ")
                file.write(method["name"] + "(")
                n = 0
                no_param = False
                for arg in method["arguments"]:
                    if arg["IdlType"] == "void":
                        no_param = True
                        break;
                    if arg["IdlType"] == "seq" or arg["IdlType"] == "set":
                        file.write(arg["type"] + "<" + arg["key"]["type"] + ">")
                    elif arg["IdlType"] == "dict":
                        file.write(arg["type"] + "<" + arg["key"]["type"] + "," + arg["value"]["type"] + ">")
                    elif arg["IdlType"] == "struct" or arg["IdlType"] == "string":
                        file.write(arg["type"])
                    else:
                        file.write(arg["type"])
                    n += 1
                    file.write(" arg" + str(n))
                    if n != len(method["arguments"]):
                        file.write(",")
                if not ("oneway" in method):
                    if no_param:
                        file.write("long timeout = " + str(method["timeout"]) + ") {\n")
                    else:
                        file.write(", long timeout = " + str(method["timeout"]) + ") {\n")
                else:
                    file.write(") {\n")
                file.write("        var param = ObjectPool<" + service["name"] + "_" + method["name"] + "_CallParam>.Get();\n")
                file.write("        param.IsDebugLoop = debug_loop_;\n")
                if not no_param:
                    n = 0
                    for arg in method["arguments"]:
                        n += 1
                        file.write("        param.arg" + str(n) + " = arg" + str(n) + ";\n")
                file.write("        param.CallID = CallIDGenerator.generate();\n")
                if method["retType"]["IdlType"] == "void":
                    if not ("oneway" in method):                        
                        file.write("        var awaiter = ObjectPool<ProxyCallAwaiter>.Get();\n")
                        file.write("        awaiter.ServiceUUID = " + service["uuid"] + ";\n")
                        file.write("        awaiter.MethodID = " + str(method["index"]) + ";\n")
                        file.write("        awaiter.CallID = param.CallID;\n")
                        file.write("        awaiter.Timeout = timeout;\n")
                        file.write("        awaiter.DebugLoop = debug_loop_;\n")
                        file.write("        if (debug_loop_) {\n")
                        file.write("            Singleton<Rpc>.Instance.OnMessage(param);\n")
                        file.write("        } else {\n")
                        file.write("            client_.Send(param);\n")
                        file.write("            await awaiter;\n")
                        file.write("        }\n")
                    else:
                        file.write("        if (debug_loop_) {\n")
                        file.write("            Singleton<Rpc>.Instance.OnMessage(param);\n")
                        file.write("        } else {\n")
                        file.write("            client_.Send(param);\n")
                        file.write("        }\n")
                else:
                    file.write("        var awaiter = ObjectPool<ProxyCallAwaiter<" + ret_string + ">>.Get();\n")
                    file.write("        awaiter.ServiceUUID = " + service["uuid"] + ";\n")
                    file.write("        awaiter.MethodID = " + str(method["index"]) + ";\n")
                    file.write("        awaiter.CallID = param.CallID;\n")
                    file.write("        awaiter.Timeout = timeout;\n")
                    file.write("        awaiter.DebugLoop = debug_loop_;\n")
                    file.write("        if (debug_loop_) {\n")
                    file.write("            Singleton<Rpc>.Instance.OnMessage(param);\n")
                    file.write("            return awaiter.GetResult();\n")
                    file.write("        } else {\n")
                    file.write("            client_.Send(param);\n")
                    file.write("            return await awaiter;\n")
                    file.write("        }\n")
                file.write("    }\n")
            file.write("}\n\n")
            file.write("}\n\n")

    def gen_param_return_register(self, file):
        if (not ("services" in self.cpp_json)) or (self.cpp_json["services"] is None):
            return
        file.write("namespace rpc {\n\n")
        file.write("    public static class " + self.file_name_split[0] + "Register {\n")
        file.write("        public static void DoRegisterMethodParam() {\n")
        for service in self.cpp_json["services"]:
            file.write("            Singleton<ParamRegister>.Instance.Register.Add(" + service["uuid"] + ", new Dictionary<uint, NewParamByUUID>());\n")
            n = 1;
            for method in service["methods"]:
                file.write("            Singleton<ParamRegister>.Instance.Register[" + service["uuid"] + "].Add(" + str(method["index"]) + ", "+self.file_name_split[0]+'.' + service["name"] + "_" + method["name"] + "_delegate.NewParam);\n")
                n += 1
            file.write("\n")
        file.write("        }\n")
        file.write("        public static void DoRegisterMethodReturn() {\n")
        for service in self.cpp_json["services"]:
            file.write("            Singleton<ReturnRegister>.Instance.Register.Add(" + service["uuid"] + ", new Dictionary<uint, NewReturnByUUID>());\n")
            n = 1;
            for method in service["methods"]:
                file.write("            Singleton<ReturnRegister>.Instance.Register[" + service["uuid"] + "].Add(" + str(method["index"]) + ", " +self.file_name_split[0]+'.' + service["name"] + "_" + method["name"] + "_delegate.NewReturn);\n")
                n += 1
            file.write("\n")
        file.write("        }\n")
        file.write("    }\n")

        file.write("}\n\n")

    def gen_rpc_register(self):
        file = None
        start_str = "//"+ self.file_name_split[0] + "->"
        end_str = "//"+ self.file_name_split[0] + "<-"
        last_str = "//->//<-"
        if not os.path.exists("rpc.register.cs"):
            file = open("rpc.register.cs", "w")
            file.write("#region Machine generated code\n\n")
            file.write("// Machine generated code\n\n")
            file.write("namespace rpc {\n\n")
            file.write("    internal static class RpcRegister {\n")
            file.write("        internal static void DoRegister() {\n")
            file.write(start_str + "\n") 
            file.write("            " + self.file_name_split[0] + "Register.DoRegisterMethodParam();\n")
            file.write("            " + self.file_name_split[0] + "Register.DoRegisterMethodReturn();\n")
            file.write("            " + self.file_name_split[0] + "Caller.DoCallerRegister();\n")
            file.write(end_str)
            file.write("\n" + last_str + "\n")
            file.write("        }\n")
            file.write("    }\n")
            file.write("}\n\n")
            file.write("#endregion Machine generated code\n\n")
            file.close()
            return
        content = open("rpc.register.cs", "r").read()
        file = open("rpc.register.cs", "r+")
        insert_str = start_str + "\n"
        insert_str += "            " + self.file_name_split[0] + "Register.DoRegisterMethodParam();\n"
        insert_str += "            " + self.file_name_split[0] + "Register.DoRegisterMethodReturn();\n"
        insert_str += "            " + self.file_name_split[0] + "Caller.DoCallerRegister();\n"
        insert_str += end_str
        start = content.find(insert_str)
        if start == -1:
            insert_str += "\n" + last_str + "\n"
            file.write(content.replace(last_str, insert_str))
        file.close()

    def gen_service_caller(self, file):
        if (not ("services" in self.cpp_json)) or (self.cpp_json["services"] is None):
            return
        file.write("namespace rpc {\n\n")
        file.write("internal static class " + self.file_name_split[0] + "Caller {\n")
        for service in self.cpp_json["services"]:
            no_service = False
            if "notations" in service:
                for notation in service["notations"]:
                    if "no_service" in notation:
                        for name in notation["no_service"]:
                            if name == "csharp":
                                no_service = True
                                break
            if no_service:
                continue
            for method in service["methods"]:
                file.write("    internal static void " + service["name"] + "_" + method["name"] + "(IClient client, CallParam param, IService service) {\n")
                file.write("        " + service["name"] + "Impl impl = (" + service["name"] + "Impl)service;\n")
                file.write("        " +self.file_name_split[0]+'.'+ service["name"] + "_" + method["name"] + "_CallParam param_impl = ("+self.file_name_split[0]+'.'+ service["name"] + "_" + method["name"] + "_CallParam)param;\n")
                ret_string = ""
                if method["retType"]["IdlType"] != "void":
                    file.write("        " +self.file_name_split[0]+'.'+ service["name"] + "_" + method["name"] + "_CallReturn ret ="+ self.file_name_split[0] + "." + service["name"] + "_" + method["name"] +"_delegate.NewReturn();\n")
                    file.write("        try {\n")
                    file.write("            ret = kratos.ObjectPool<"+self.file_name_split[0]+'.' + service["name"] + "_" + method["name"] + "_CallReturn>.Get();\n")
                    if method["retType"]["IdlType"] == "seq" or method["retType"]["IdlType"] == "set":
                        ret_string = method["retType"]["type"] + "<" + method["retType"]["key"]["type"] + ">"
                    elif method["retType"]["IdlType"] == "dict":
                        ret_string = method["retType"]["type"] + "<" + method["retType"]["key"]["type"] + "," + method["retType"]["value"]["type"] + ">"
                    elif method["retType"]["IdlType"] == "string" or method["retType"]["IdlType"] == "struct":
                        ret_string = method["retType"]["type"]
                    else:
                        ret_string = method["retType"]["type"]
                    file.write("            var retval = impl." + method["name"] +"(")
                    n = 1
                    for arg in method["arguments"]:
                        if arg["IdlType"] == "void":
                            break;
                        if n < len(method["arguments"]):
                            file.write("param_impl.arg" + str(n) + ", ")
                        else:
                            file.write("param_impl.arg" + str(n))
                        n += 1
                    file.write(");\n")
                    file.write("            ret.ret = retval;\n")
                else:
                    if not ("oneway" in method):
                        file.write("        " +self.file_name_split[0]+'.'+ service["name"] + "_" + method["name"] + "_CallReturn ret = "+ self.file_name_split[0] + "." + service["name"] + "_" + method["name"] +"_delegate.NewReturn();\n")
                    file.write("        try {\n")
                    file.write("            impl." + method["name"] +"(")
                    n = 1
                    for arg in method["arguments"]:
                        if arg["IdlType"] == "void":
                            break;
                        if n < len(method["arguments"]):
                            file.write("param_impl.arg" + str(n) + ", ")
                        else:
                            file.write("param_impl.arg" + str(n))
                        n += 1
                    file.write(");\n")
                if not ("oneway" in  method):
                    file.write("            ret.ErrorID = (uint)RpcError.rpc_ok;\n")                
                file.write("        } catch (Exception) {\n")
                if not ("oneway" in method):
                    file.write("            ret.ErrorID = (uint)RpcError.rpc_exception;\n")
                file.write("        }\n")
                if not ("oneway" in method):
                    file.write("        ret.CallID = param.CallID;\n")
                    file.write("        ret.ServiceID = service.ID;\n")
                    file.write("        if (param.IsDebugLoop) {\n")                    
                    if method["retType"]["IdlType"] != "void":
                        file.write("            var waiter = (ProxyCallAwaiter<" + ret_string + ">)Singleton<ManagerThreaded<uint, ProxyCall>>.Instance.Get(param.CallID);\n")
                        file.write("            waiter.SetResult(ret.ret);\n")
                        file.write("            Singleton<Rpc>.Instance.OnMessage(ret);\n")
                    else:
                        if not ("oneway" in method):
                            file.write("            Singleton<Rpc>.Instance.OnMessage(ret);\n")
                    file.write("        } else {\n")
                    file.write("            client.Send(ret);\n")
                    file.write("        }\n")
                file.write("    }\n")
        file.write("    public static void DoCallerRegister() {\n")
        for service in self.cpp_json["services"]:
            no_service = False
            if "notations" in service:
                for notation in service["notations"]:
                    if "no_service" in notation:
                        for name in notation["no_service"]:
                            if name == "csharp":
                                no_service = True
                                break
            if no_service:
                continue
            file.write("        Singleton<ServiceManager>.Instance.RegisterService(" + service["uuid"] + ", new " + service["name"] + "Impl());\n")
            for method in service["methods"]:
                file.write("        Singleton<ServiceCaller>.Instance.Add(" + service["uuid"] + ", " + str(method["index"]) +", " + service["name"] + "_" + method["name"] + ");\n")
        file.write("    }\n")
        file.write("}\n")
        file.write("}\n\n")

if __name__ == "__main__":
    file = open(sys.argv[1])
    json = json.load(file)
    FileGenerator(sys.argv[1], json)
    